/*
 * This file is part of the URI Template library.
 *
 * For licensing information please see the file license.txt included in the release.
 * A copy of this licence can also be found at
 *   http://www.opensource.org/licenses/artistic-license-2.0.php
 */
package org.weborganic.furi;

import java.util.Hashtable;
import java.util.Map;

import org.weborganic.furi.Variable.Reserved;

/**
 * A factory for URI tokens.
 *
 * <p>
 * Tokens can be instantiated from an expression which is specific to each
 * token.
 *
 * @see TokenLiteral
 * @see TokenVariable
 * @see TokenOperatorD3
 * @see TokenOperatorPS
 *
 * @author Christophe Lauret
 * @version 6 November 2009
 */
public class TokenFactory {

	/**
	 * A syntax to use for creating tokens.
	 * 
	 * @author Christophe Lauret
	 * @version 6 November 2009
	 */
	public enum Syntax {

		/**
		 * Use the syntax defined in Draft 3 of the URI templates.
		 */
		DRAFT3 {

			@Override
			protected Token newExpansion(String exp) {
				// an operator as defined in Draft 3
				if (exp.indexOf('|') >= 0) {
					return TokenOperatorD3.parse(exp);
					// assume a variable
				} else {
					return new TokenVariable(Variable.parse(exp));
				}
			}

		},

		/**
		 * Use a syntax currently used in PageSeeder and based on what was
		 * suggested by Roy T Fielding on the W3C URI listing in October 2008.
		 */
		PAGESEEDER {

			@Override
			protected Token newExpansion(String exp) {
				// possibly Roy Fielding's operators
				if (!Character.isLetter(exp.charAt(0)) && !Character.isDigit(exp.charAt(0))) {
					return TokenOperatorPS.parse(exp);
					// assume a variable
				} else {
					return new TokenVariable(Variable.parse(exp));
				}
			}

		},

		/**
		 * Use a syntax based on the draft as of 29 October 2009.
		 */
		DRAFTX {

			@Override
			protected Token newExpansion(String exp) {
				// possibly Roy Fielding's operators
				if (!Character.isLetter(exp.charAt(0)) && !Character.isDigit(exp.charAt(0))) {
					return TokenOperatorDX.parse(exp);
					// maybe a collection
				} else if (exp.indexOf(',') >= 0) {
					return TokenOperatorDX.parse(exp);
					// assume a variable
				} else {
					return new TokenVariable(Variable.parse(exp));
				}
			}

		};

		/**
		 * Generates a template expansion token corresponding to the specified
		 * expression.
		 * 
		 * @param exp
		 *            The expression within the curly brackets {}.
		 * 
		 * @return The corresponding token instance.
		 * 
		 * @throws URITemplateSyntaxException
		 *             If the expression could not be parsed as a valid token.
		 */
		protected abstract Token newExpansion(String exp);

	}

	/**
	 * Factories for reuse.
	 */
	private static final Map<Syntax, TokenFactory> FACTORIES = new Hashtable<>();
	static {
		for (Syntax syntax : Syntax.values()) {
			FACTORIES.put(syntax, new TokenFactory(syntax));
		}
	}

	/**
	 * The URI template syntax to use for generating tokens.
	 */
	private Syntax _syntax;

	/**
	 * Prevents creation of instances.
	 * 
	 * @param syntax
	 *            The URI template syntax to use for generating tokens.
	 */
	private TokenFactory(Syntax syntax) {
		this._syntax = syntax;
	}

	/**
	 * Generates the token corresponding to the specified expression.
	 * 
	 * @param exp
	 *            The expression.
	 * 
	 * @return The corresponding token instance.
	 * 
	 * @throws URITemplateSyntaxException
	 *             If the expression could not be parsed as a valid token.
	 */
	public Token newToken(String exp) {
		// no expression: no token!
		if (exp == null || exp.length() == 0)
			return null;
		// intercept the wild card
		if ("*".equals(exp))
			return newWildcard();
		// too short to be anything but a literal
		int len = exp.length();
		if (len < 2)
			return new TokenLiteral(exp);
		// a template expansion token
		if (exp.charAt(0) == '{' && exp.charAt(len - 1) == '}') {
			// defer to the underlying syntax
			return this._syntax.newExpansion(exp.substring(1, len - 1));
		}
		// a literal text token
		return new TokenLiteral(exp);
	}

	/**
	 * Generates the token corresponding to the specified expression.
	 * 
	 * @param exp
	 *            The expression.
	 * 
	 * @return The corresponding token instance.
	 * 
	 * @throws URITemplateSyntaxException
	 *             If the expression could not be parsed as a valid token.
	 */
	public static Token newToken(String exp, Syntax syntax) {
		TokenFactory factory = getInstance(syntax);
		return factory.newToken(exp);
	}

	/**
	 * Creates a new 'wildcard' token for legacy purposes.
	 * 
	 * <p>
	 * This is used for conventional URI patterns which have been implemented
	 * using "*".
	 * 
	 * @return A new 'wildcard' token.
	 */
	private static final Token newWildcard() {
		return new TokenOperatorPS(TokenOperatorPS.Operator.URI_INSERT, new Variable(Reserved.WILDCARD));
	}

	/**
	 * Returns a token factory instance using the default syntax (DRAFTX).
	 * 
	 * @return a token factory instance using the default syntax (DRAFTX).
	 */
	public static TokenFactory getInstance() {
		return FACTORIES.get(Syntax.DRAFTX);
	}

	/**
	 * Returns a token factory instance.
	 * 
	 * @return a token factory instance.
	 */
	public static TokenFactory getInstance(Syntax syntax) {
		return new TokenFactory(syntax);
	}

}
